1   /*
2    * Copyright (C) 2009 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.google.common.collect;
18  
19  import static com.google.common.truth.Truth.assertThat;
20  
21  import com.google.common.annotations.GwtCompatible;
22  import com.google.common.annotations.GwtIncompatible;
23  
24  /**
25   * Tests common methods in {@link ImmutableTable}
26   *
27   * @author Gregory Kick
28   */
29  @GwtCompatible(emulated = true)
30  public class ImmutableTableTest extends AbstractTableReadTest {
31    @Override protected Table<String, Integer, Character> create(Object... data) {
32      ImmutableTable.Builder<String, Integer, Character> builder =
33          ImmutableTable.builder();
34      for (int i = 0; i < data.length; i = i + 3) {
35        builder.put((String) data[i], (Integer) data[i + 1],
36            (Character) data[i + 2]);
37      }
38      return builder.build();
39    }
40  
41    public void testBuilder() {
42      ImmutableTable.Builder<Character, Integer, String> builder =
43          new ImmutableTable.Builder<Character, Integer, String>();
44      assertEquals(ImmutableTable.of(), builder.build());
45      assertEquals(ImmutableTable.of('a', 1, "foo"), builder
46          .put('a', 1, "foo")
47          .build());
48      Table<Character, Integer, String> expectedTable = HashBasedTable.create();
49      expectedTable.put('a', 1, "foo");
50      expectedTable.put('b', 1, "bar");
51      expectedTable.put('a', 2, "baz");
52      Table<Character, Integer, String> otherTable = HashBasedTable.create();
53      otherTable.put('b', 1, "bar");
54      otherTable.put('a', 2, "baz");
55      assertEquals(expectedTable, builder
56          .putAll(otherTable)
57          .build());
58    }
59  
60    public void testBuilder_withImmutableCell() {
61      ImmutableTable.Builder<Character, Integer, String> builder =
62          new ImmutableTable.Builder<Character, Integer, String>();
63      assertEquals(ImmutableTable.of('a', 1, "foo"), builder
64          .put(Tables.immutableCell('a', 1, "foo"))
65          .build());
66    }
67  
68    public void testBuilder_withImmutableCellAndNullContents() {
69      ImmutableTable.Builder<Character, Integer, String> builder =
70          new ImmutableTable.Builder<Character, Integer, String>();
71      try {
72        builder.put(Tables.immutableCell((Character) null, 1, "foo"));
73        fail();
74      } catch (NullPointerException e) {
75        // success
76      }
77      try {
78        builder.put(Tables.immutableCell('a', (Integer) null, "foo"));
79        fail();
80      } catch (NullPointerException e) {
81        // success
82      }
83      try {
84        builder.put(Tables.immutableCell('a', 1, (String) null));
85        fail();
86      } catch (NullPointerException e) {
87        // success
88      }
89    }
90  
91    private static class StringHolder {
92      String string;
93    }
94  
95    public void testBuilder_withMutableCell() {
96      ImmutableTable.Builder<Character, Integer, String> builder =
97          new ImmutableTable.Builder<Character, Integer, String>();
98  
99      final StringHolder holder = new StringHolder();
100     holder.string = "foo";
101     Table.Cell<Character, Integer, String> mutableCell =
102         new Tables.AbstractCell<Character, Integer, String>() {
103           @Override public Character getRowKey() {
104             return 'K';
105           }
106           @Override public Integer getColumnKey() {
107             return 42;
108           }
109           @Override public String getValue() {
110             return holder.string;
111           }
112         };
113 
114     // Add the mutable cell to the builder
115     builder.put(mutableCell);
116 
117     // Mutate the value
118     holder.string = "bar";
119 
120     // Make sure it uses the original value.
121     assertEquals(ImmutableTable.of('K', 42, "foo"), builder.build());
122   }
123 
124   public void testBuilder_noDuplicates() {
125     ImmutableTable.Builder<Character, Integer, String> builder =
126         new ImmutableTable.Builder<Character, Integer, String>()
127             .put('a', 1, "foo")
128             .put('a', 1, "bar");
129     try {
130       builder.build();
131       fail();
132     } catch (IllegalArgumentException e) {
133       // success
134     }
135   }
136 
137   public void testBuilder_noNulls() {
138     ImmutableTable.Builder<Character, Integer, String> builder =
139         new ImmutableTable.Builder<Character, Integer, String>();
140     try {
141       builder.put(null, 1, "foo");
142       fail();
143     } catch (NullPointerException e) {
144       // success
145     }
146     try {
147       builder.put('a', null, "foo");
148       fail();
149     } catch (NullPointerException e) {
150       // success
151     }
152     try {
153       builder.put('a', 1, null);
154       fail();
155     } catch (NullPointerException e) {
156       // success
157     }
158   }
159 
160   private static <R, C, V> void validateTableCopies(Table<R, C, V> original) {
161     Table<R, C, V> copy = ImmutableTable.copyOf(original);
162     assertEquals(original, copy);
163     validateViewOrdering(original, copy);
164 
165     Table<R, C, V> built
166         = ImmutableTable.<R, C, V>builder().putAll(original).build();
167     assertEquals(original, built);
168     validateViewOrdering(original, built);
169   }
170 
171   private static <R, C, V> void validateViewOrdering(
172       Table<R, C, V> original, Table<R, C, V> copy) {
173     assertTrue(Iterables.elementsEqual(original.cellSet(), copy.cellSet()));
174     assertTrue(Iterables.elementsEqual(original.rowKeySet(), copy.rowKeySet()));
175     assertTrue(Iterables.elementsEqual(original.values(), copy.values()));
176   }
177 
178   public void testCopyOf() {
179     Table<Character, Integer, String> table = TreeBasedTable.create();
180     validateTableCopies(table);
181     table.put('b', 2, "foo");
182     validateTableCopies(table);
183     table.put('b', 1, "bar");
184     table.put('a', 2, "baz");
185     validateTableCopies(table);
186     // Even though rowKeySet, columnKeySet, and cellSet have the same
187     // iteration ordering, row has an inconsistent ordering.
188     assertThat(table.row('b').keySet()).has().exactly(1, 2).inOrder();
189     assertThat(ImmutableTable.copyOf(table).row('b').keySet())
190         .has().exactly(2, 1).inOrder();
191   }
192 
193   public void testCopyOfSparse() {
194     Table<Character, Integer, String> table = TreeBasedTable.create();
195     table.put('x', 2, "foo");
196     table.put('r', 1, "bar");
197     table.put('c', 3, "baz");
198     table.put('b', 7, "cat");
199     table.put('e', 5, "dog");
200     table.put('c', 0, "axe");
201     table.put('e', 3, "tub");
202     table.put('r', 4, "foo");
203     table.put('x', 5, "bar");
204     validateTableCopies(table);
205   }
206 
207   public void testCopyOfDense() {
208     Table<Character, Integer, String> table = TreeBasedTable.create();
209     table.put('c', 3, "foo");
210     table.put('c', 2, "bar");
211     table.put('c', 1, "baz");
212     table.put('b', 3, "cat");
213     table.put('b', 1, "dog");
214     table.put('a', 3, "foo");
215     table.put('a', 2, "bar");
216     table.put('a', 1, "baz");
217     validateTableCopies(table);
218   }
219 
220   public void testBuilder_orderRowsAndColumnsBy_putAll() {
221     Table<Character, Integer, String> table = HashBasedTable.create();
222     table.put('b', 2, "foo");
223     table.put('b', 1, "bar");
224     table.put('a', 2, "baz");
225     ImmutableTable.Builder<Character, Integer, String> builder
226         = ImmutableTable.builder();
227     Table<Character, Integer, String> copy
228         = builder.orderRowsBy(Ordering.natural())
229             .orderColumnsBy(Ordering.natural())
230             .putAll(table).build();
231     assertThat(copy.rowKeySet()).has().exactly('a', 'b').inOrder();
232     assertThat(copy.columnKeySet()).has().exactly(1, 2).inOrder();
233     assertThat(copy.values()).has().exactly("baz", "bar", "foo").inOrder();
234     assertThat(copy.row('b').keySet()).has().exactly(1, 2).inOrder();
235   }
236 
237   public void testBuilder_orderRowsAndColumnsBy_sparse() {
238     ImmutableTable.Builder<Character, Integer, String> builder
239         = ImmutableTable.builder();
240     builder.orderRowsBy(Ordering.natural());
241     builder.orderColumnsBy(Ordering.natural());
242     builder.put('x', 2, "foo");
243     builder.put('r', 1, "bar");
244     builder.put('c', 3, "baz");
245     builder.put('b', 7, "cat");
246     builder.put('e', 5, "dog");
247     builder.put('c', 0, "axe");
248     builder.put('e', 3, "tub");
249     builder.put('r', 4, "foo");
250     builder.put('x', 5, "bar");
251     Table<Character, Integer, String> table = builder.build();
252     assertThat(table.rowKeySet()).has().exactly('b', 'c', 'e', 'r', 'x').inOrder();
253     assertThat(table.columnKeySet()).has().exactly(0, 1, 2, 3, 4, 5, 7).inOrder();
254     assertThat(table.values()).has().exactly("cat", "axe", "baz", "tub",
255         "dog", "bar", "foo", "foo", "bar").inOrder();
256     assertThat(table.row('c').keySet()).has().exactly(0, 3).inOrder();
257     assertThat(table.column(5).keySet()).has().exactly('e', 'x').inOrder();
258   }
259 
260   public void testBuilder_orderRowsAndColumnsBy_dense() {
261     ImmutableTable.Builder<Character, Integer, String> builder
262         = ImmutableTable.builder();
263     builder.orderRowsBy(Ordering.natural());
264     builder.orderColumnsBy(Ordering.natural());
265     builder.put('c', 3, "foo");
266     builder.put('c', 2, "bar");
267     builder.put('c', 1, "baz");
268     builder.put('b', 3, "cat");
269     builder.put('b', 1, "dog");
270     builder.put('a', 3, "foo");
271     builder.put('a', 2, "bar");
272     builder.put('a', 1, "baz");
273     Table<Character, Integer, String> table = builder.build();
274     assertThat(table.rowKeySet()).has().exactly('a', 'b', 'c').inOrder();
275     assertThat(table.columnKeySet()).has().exactly(1, 2, 3).inOrder();
276     assertThat(table.values()).has().exactly("baz", "bar", "foo", "dog",
277         "cat", "baz", "bar", "foo").inOrder();
278     assertThat(table.row('c').keySet()).has().exactly(1, 2, 3).inOrder();
279     assertThat(table.column(1).keySet()).has().exactly('a', 'b', 'c').inOrder();
280   }
281 
282   public void testBuilder_orderRowsBy_sparse() {
283     ImmutableTable.Builder<Character, Integer, String> builder
284         = ImmutableTable.builder();
285     builder.orderRowsBy(Ordering.natural());
286     builder.put('x', 2, "foo");
287     builder.put('r', 1, "bar");
288     builder.put('c', 3, "baz");
289     builder.put('b', 7, "cat");
290     builder.put('e', 5, "dog");
291     builder.put('c', 0, "axe");
292     builder.put('e', 3, "tub");
293     builder.put('r', 4, "foo");
294     builder.put('x', 5, "bar");
295     Table<Character, Integer, String> table = builder.build();
296     assertThat(table.rowKeySet()).has().exactly('b', 'c', 'e', 'r', 'x').inOrder();
297     assertThat(table.column(5).keySet()).has().exactly('e', 'x').inOrder();
298   }
299 
300   public void testBuilder_orderRowsBy_dense() {
301     ImmutableTable.Builder<Character, Integer, String> builder
302         = ImmutableTable.builder();
303     builder.orderRowsBy(Ordering.natural());
304     builder.put('c', 3, "foo");
305     builder.put('c', 2, "bar");
306     builder.put('c', 1, "baz");
307     builder.put('b', 3, "cat");
308     builder.put('b', 1, "dog");
309     builder.put('a', 3, "foo");
310     builder.put('a', 2, "bar");
311     builder.put('a', 1, "baz");
312     Table<Character, Integer, String> table = builder.build();
313     assertThat(table.rowKeySet()).has().exactly('a', 'b', 'c').inOrder();
314     assertThat(table.column(1).keySet()).has().exactly('a', 'b', 'c').inOrder();
315   }
316 
317   public void testBuilder_orderColumnsBy_sparse() {
318     ImmutableTable.Builder<Character, Integer, String> builder
319         = ImmutableTable.builder();
320     builder.orderColumnsBy(Ordering.natural());
321     builder.put('x', 2, "foo");
322     builder.put('r', 1, "bar");
323     builder.put('c', 3, "baz");
324     builder.put('b', 7, "cat");
325     builder.put('e', 5, "dog");
326     builder.put('c', 0, "axe");
327     builder.put('e', 3, "tub");
328     builder.put('r', 4, "foo");
329     builder.put('x', 5, "bar");
330     Table<Character, Integer, String> table = builder.build();
331     assertThat(table.columnKeySet()).has().exactly(0, 1, 2, 3, 4, 5, 7).inOrder();
332     assertThat(table.row('c').keySet()).has().exactly(0, 3).inOrder();
333   }
334 
335   public void testBuilder_orderColumnsBy_dense() {
336     ImmutableTable.Builder<Character, Integer, String> builder
337         = ImmutableTable.builder();
338     builder.orderColumnsBy(Ordering.natural());
339     builder.put('c', 3, "foo");
340     builder.put('c', 2, "bar");
341     builder.put('c', 1, "baz");
342     builder.put('b', 3, "cat");
343     builder.put('b', 1, "dog");
344     builder.put('a', 3, "foo");
345     builder.put('a', 2, "bar");
346     builder.put('a', 1, "baz");
347     Table<Character, Integer, String> table = builder.build();
348     assertThat(table.columnKeySet()).has().exactly(1, 2, 3).inOrder();
349     assertThat(table.row('c').keySet()).has().exactly(1, 2, 3).inOrder();
350   }
351 
352   @GwtIncompatible("Mind-bogglingly slow in GWT")
353   public void testOverflowCondition() {
354     // See https://code.google.com/p/guava-libraries/issues/detail?id=1322 for details.
355     ImmutableTable.Builder<Integer, Integer, String> builder = ImmutableTable.builder();
356     for (int i = 1; i < 0x10000; i++) {
357       builder.put(i, 0, "foo");
358       builder.put(0, i, "bar");
359     }
360     assertTrue(builder.build() instanceof SparseImmutableTable);
361   }
362 }